package com.huawei.boss.xml;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 使用递推回溯来实现
 * @author c00170351
 *
 */
public class RecurrenceXmltransStrategy extends DomXmltransStrategy {
    //增加一个哨兵，减少算法边界检测的工作量
    private static final XmlElem NIL_ELEM = new XmlElem(-1, Integer.MIN_VALUE, "", "");

    /**
     * 从xml转换成列表,使用递推回溯来实现
     * @param root 待转换的报文根节点
     * @return 返回转换好的列表信息
     */
    @Override
    public List<XmlElem> parseRootNode(Element root) {
        //保存待处理的节点和该节点的父节点位置
        Stack<Node> todoNodes = new Stack<Node>();
        Stack<Integer> parentpos = new Stack<Integer>();
        todoNodes.add(root);
        parentpos.add(-1);

        int currSeq = 0;
        List<XmlElem> xmlElems = new ArrayList<XmlElem>();
        while (!todoNodes.isEmpty()) {
            Node currNode = todoNodes.pop();
            int parentSeq = parentpos.pop();

            String tagName = currNode.getNodeName();
            NodeList childNodes = currNode.getChildNodes();
            int length = childNodes.getLength();

            //判断当前是否是内容节点
            boolean textOnly = true;
            //把所有的子节点反序放入栈中
            for (int i = length - 1; i >= 0; i--) {
                Node node = childNodes.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    todoNodes.push(node);
                    parentpos.push(currSeq);
                    textOnly = false;
                }
            }

            String content = textOnly ? currNode.getTextContent() : "";
            xmlElems.add(new XmlElem(currSeq, parentSeq, tagName, content));
            //下一个节点的序号递增
            currSeq++;
        }
        return xmlElems;
    }

    /**
     * 从列表转换成xml,使用递推的写法
     * @param xmlElems 待转换的列表信息，并假设已经根据currSeq进行了排序
     * @return 返回xml报文
     */
    public String list2xml(List<XmlElem> xmlElems) {
        StringBuffer sb = new StringBuffer();
        Stack<XmlElem> endTagXmlElems = new Stack<XmlElem>();
        endTagXmlElems.push(NIL_ELEM);

        for (XmlElem xmlElem : xmlElems) {
            //先保存上一个节点，以便判断是否结束子节点输出
            XmlElem top = endTagXmlElems.peek();
            while (xmlElem.getParentSeq() != top.getCurrSeq()) {
                //结束,应该输出top节点
                top = endTagXmlElems.pop();
                sb.append("</" + top.getTagName() + ">");
                top = endTagXmlElems.peek();
            }

            //对每个元素，总是先输出开始节点
            sb.append("<" + xmlElem.getTagName() + ">");

            sb.append(xmlElem.getTagTxt());
            endTagXmlElems.push(xmlElem);
        }

        while (!endTagXmlElems.peek().equals(NIL_ELEM)) {
            sb.append("</" + endTagXmlElems.pop().getTagName() + ">");
        }
        return sb.toString();
    }
}
